/**********************************************************************************
Copyright (C), by AppleCai
Project                      : Study Kernel
Description                  : BSP level for US100
History                      :
CPU and Compiler             : AM335x,ARM-LINIX-GCC
GenDate                      : 2020-11-26
OwnerBy                      : Apple Cai
**********************************************************************************/
#define DEBUG
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/fs.h>  
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/time.h>
#include <linux/uaccess.h>
#include <linux/cdev.h> 
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/miscdevice.h>
#include <linux/gpio/consumer.h> 
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/wait.h>
#include <linux/sched/signal.h>
#include <linux/delay.h>
#include <linux/ktime.h>
#include <linux/pwm.h>
/* misc sensor US100 设备结构体 */
struct miscUS100{
dev_t devid;                /* 设备号 */
struct device *device;      /* 设备描述符 */
struct device_node *nd;     /* 设备节点 */
struct gpio_desc *cmdpin;   /* triq 所使用的GPIO描述符 */
struct gpio_desc *echopin;  /* echo 所使用的GPIO描述符 */
int irq;                    /* gpio echo申请的中断号 */
int testcnt;                /* 双边沿进入次数 */
wait_queue_head_t r_wait;   /* 等待队列 */
int echoready;             /* echo的距离是否准备完成了 */
u64 distance;     /* 计算的障碍物举例 */
u64 now;
u64 last;
spinlock_t lock; /* 定义自旋锁 */
struct miscdevice miscdev;
struct pwm_device *pwm;
struct semaphore sem;
};

//struct miscUS100 miscUS100_dev; /* 设备定义 */

//irq下半部在内核线程中
static irqreturn_t us100_irq_thread_handler(int irq, void *devdata)
{
	struct miscUS100 *dev=devdata;
	if(dev->testcnt == 2)
	{
		dev->distance = dev->now-dev->last;
		dev_dbg(dev->device,"last is %lld,now is %lld,dis:%lld",dev->last,dev->now,dev->distance);
		//实际距离 
		spin_lock_bh(&dev->lock);
		dev->distance = dev->distance*17;
		do_div(dev->distance, 100000);
		dev->echoready = 1;
		spin_unlock_bh(&dev->lock);
		//printk("dis:%lld\n",dev->distance);
		wake_up_interruptible(&dev->r_wait); /* get result then wakeup */
	}
	else
	{
	}		
	enable_irq(irq);
	return IRQ_HANDLED;
}

//irq上半部在中断中
static irqreturn_t us100_irq_handler(int irq, void *devdata)
{
	struct miscUS100 *dev=devdata;
	disable_irq_nosync(irq);
	if(dev->testcnt == 0)
	{
		/*---通过结构体中成员的首地址获结构体变量首地址---*/
		//struct us100_data* data = container_of(work,struct us100_data,report_work);
		//时间格式转换
	    dev->last=ktime_get();
		pwm_disable(dev->pwm);
	    dev->testcnt++;
	}
	else if(dev->testcnt == 1)
	{
		//时间差
		dev->now = ktime_get();
		dev->testcnt++;
	}
	else
	{

	}
	return IRQ_WAKE_THREAD;
}
/*
 * @description		: 打开设备
 * @param - inode 	: 传递给驱动的inode
 * @param - filp 	: 设备文件，file结构体有个叫做private_data的成员变量
 * 					  一般在open的时候将private_data指向设备结构体。
 * @return 			: 0 成功;其他 失败
 */

static int AppleUS100_open(struct inode *inode, struct file *filp)
{
	int ret = 0;
	struct miscUS100 *dev = container_of(filp->private_data,struct miscUS100, miscdev);
	if (!down_trylock(&dev->sem)) 
	{
		dev->testcnt = 0;
		dev->echoready = 0;
		dev->distance = 0;
		dev->last= 0;
		dev->now= 0;
		//printk("dev->last = %d",dev->last);
		/* 已经请求过了，设置triq pin为输出，并且输出低电平，因为默认为关 */
		#if 0
		ret = gpiod_direction_output(dev->cmdpin, 0);  
		if(ret < 0) {
			printk("can't set triq gpio!\r\n");
		}
		#endif
		dev->pwm = pwm_request(0, "mypwmdev");
		/*US-100 超声波测距模块可实现 2cm~4.5m。4500mm为最大距离，2倍距离就是9000毫米
		  声速1秒340米。1毫秒/340毫米。9000毫米需要26.47毫秒。所以周期设置为30ms，确保echo rx脚已经完成一次距离检测，才能启动下一次read */
		pwm_config(dev->pwm, 10000, 30000000); /* 周期30ms,占空比10us */
		if (NULL == dev->pwm)
		{
				printk("no res\n");
				return -ENODEV;
		}
		ret = gpiod_direction_input(dev->echopin); /* echo pin设置方向为输入 */
		if(ret < 0) {
			printk("can't set echo gpio!\r\n");
		}
		/* 设置中断处理函数 */
		dev->irq = gpiod_to_irq(dev->echopin);
		if(dev->irq){
	        /*ret = devm_request_irq(dev->device,miscUS100_dev.irq, us100_irq_handler,
	         IRQF_SHARED, "us100-echo-gpio", &miscUS100_dev);*/
			ret=request_threaded_irq(dev->irq,us100_irq_handler,us100_irq_thread_handler,IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING|IRQF_ONESHOT,"AppleUS100_Device",dev);
	        if (ret != 0) {
				//free_irq(miscUS100_dev.irq, NULL); //devm_request_irq函数内部已经free_irq,外部则不需要了。
	            dev_err(dev->device, "Failed to request IRQ: %d\n", ret);
				return ret;
			}
			else
			{
				dev_dbg(dev->device,"irq num is %d",dev->irq);
			}
		}
		else
		{
			//printk("free gpio-irq\n");
			//gpiod_free(dev->echopin);
			return -EINVAL;
		}
		filp->private_data = dev; /* 挂载私有数据 */
	}
	else
	{
		printk("US100 is busy\n");
        return -EBUSY;
	}

	return ret;
}
int AppleUS100_release(struct inode *inode,struct file *filp)  
{  
	struct miscUS100 *dev = filp->private_data;
	up(&dev->sem); 
	pwm_disable(dev->pwm);
	pwm_free(dev->pwm);
	disable_irq(dev->irq);  //中断不可用
	free_irq(dev->irq, dev);
	#if 0
	if(miscUS100_dev.irq)
	{
		printk("free gpio-irq\n");
		gpiod_free(dev->echopin);
	}
	/* 注销设备的时候将cmdpin设置为低电平,0:inactive,1:active*/
	gpiod_set_value_cansleep(miscUS100_dev.cmdpin, 0);
	#endif
 	return 0;  
}  
/*设备控制函数：*/ 
static ssize_t AppleUS100_read(struct file *filp,char __user *buf,size_t size,loff_t *ppos) 
{  
	int ret = 0;
	unsigned long flags;
	struct miscUS100 *dev = filp->private_data;
#if 0
	/* 设置triq pin为输出高电平维持12us */
	ret = gpiod_direction_output(dev->cmdpin, 1);  
	if(ret < 0) {
		printk("can't set triq gpio!\r\n");
	}
	udelay(10);
	ret = gpiod_direction_output(dev->cmdpin, 0);  
	if(ret < 0) {
		printk("can't set triq gpio!\r\n");
	}
#endif

	pwm_enable(dev->pwm);
	 /*support nonblock*/
	 
	 if(filp->f_flags & O_NONBLOCK)
	 {
		 if(dev->echoready==0) 
		 {
			return -EAGAIN;
		 }
	 }
	 else
	 {
		ret = wait_event_interruptible(dev->r_wait, dev->echoready);
		if (ret) {
			return -EAGAIN;
		}
		#if 0
		DECLARE_WAITQUEUE(wait, current);	/* 定义一个等待队列 */
		if(dev->echoready == 0) /* 没有计算完成 */
		{	
			add_wait_queue(&dev->r_wait, &wait);	/* 将等待队列添加到等待队列头 */
			__set_current_state(TASK_INTERRUPTIBLE);/* 设置任务状态 */
			schedule();							/* 进行一次任务切换 */
			if(signal_pending(current))	{			/* 判断是否为信号引起的唤醒 */
				ret = -ERESTARTSYS;
				goto wait_error;
			}
			__set_current_state(TASK_RUNNING);      /* 将当前任务设置为运行状态 */
			remove_wait_queue(&dev->r_wait, &wait);    /* 将对应的队列项从等待队列头删除 */
		}
		#endif
	 }
	spin_lock_irqsave(&dev->lock, flags);
	dev->echoready=0;
	ret = copy_to_user(buf, &dev->distance, sizeof(dev->distance));
	spin_unlock_irqrestore(&dev->lock, flags);
	//printk("copy value is %lld",dev->distance);
	return ret;
}  

/* AppleUS100操作函数 */
static const struct file_operations AppleUS100_ops = {
	.owner = THIS_MODULE,
	.open = AppleUS100_open,
	.read = AppleUS100_read,
	.release = AppleUS100_release,
};


static int appleus100_probe(struct platform_device *dev)
{
    struct miscUS100 *us100dev;
	int ret = 0;
	us100dev = kzalloc( sizeof(struct miscUS100), GFP_KERNEL);
    if (!us100dev)
		return -ENOMEM;
	dev_set_drvdata(&dev->dev,us100dev);
	us100dev->device = &dev->dev;
     
	/* 获取设备节点*/
	us100dev->nd = of_find_node_by_path("/ussensor0");
	if(us100dev->nd == NULL) {
		printk("node not find!\r\n");
		return -EINVAL;
	} 

	#if 0
	/* 获取设备树中的gpio属性，得到gpio号 */
	us100dev->cmdpin = devm_gpiod_get_optional(&dev->dev, "triq", GPIOD_OUT_LOW);
    if (us100dev->cmdpin==NULL)
    {
        dev_err(&dev->dev, "Failed to get triq-gpios\n");
    }
    else
    {
    	printk("miscUS100_dev.cmdpin is %d",desc_to_gpio(us100dev->cmdpin));
    }
    #endif
	us100dev->echopin = devm_gpiod_get_optional(&dev->dev, "echo", GPIOD_IN);
    if (us100dev->echopin==NULL)
    {
        dev_err(&dev->dev, "Failed to get echo-gpios\n");
    }
    else
    {
    	printk("miscUS100_dev.echopin is %d",desc_to_gpio(us100dev->echopin));
    }
	init_waitqueue_head(&us100dev->r_wait);
	sema_init(&us100dev->sem,1);
	/* 注册MISC设备 */
	us100dev->miscdev.minor = MISC_DYNAMIC_MINOR;
	us100dev->miscdev.name = "AppleUS100";
	us100dev->miscdev.fops = &AppleUS100_ops;
	//us100dev->miscdev.parent = us100dev->device;
	ret = misc_register(&us100dev->miscdev);
	if(ret < 0){
		printk("misc device register failed!\r\n");
		return -EFAULT;
	}
	else
	{
			//us100dev->devid = MKDEV(10,us100dev->miscdev.minor);
			//printk("us100 device id min is %d",miscdev.minor);
	}
	
	return 0;
}

static int appleus100_remove(struct platform_device *dev)
{
	struct miscUS100 *us100dev = dev_get_drvdata(&dev->dev);
	/* 注销设备的时候将cmdpin设置为低电平 */
	//gpiod_set_value_cansleep(miscUS100_dev.cmdpin, 0);
	//free_irq(miscUS100_dev.irq,&dev->dev);
	/* 注销misc设备 */
	kfree(us100dev);
	misc_deregister(&us100dev->miscdev);
	return 0;
}

static const struct of_device_id AppleUS100_of_match[] = {
	{ .compatible = "applecai,AppleUS100" },
	{ /* NULL */ }
};




 /* platform驱动结构体 */
static struct platform_driver appleus100_driver = {
     .driver     = {  
     	 .name = "appleUS100",
         .of_match_table = AppleUS100_of_match, 
     },
     .probe      = appleus100_probe,
     .remove     = appleus100_remove,
};		   
static int __init AppleUS100_init(void)
{
    return platform_driver_register(&appleus100_driver);
}

static void __exit AppleUS100_exit(void)
	{

	platform_driver_unregister(&appleus100_driver);
}


module_init(AppleUS100_init);
module_exit(AppleUS100_exit);
MODULE_AUTHOR("AppleCai");  
MODULE_LICENSE("GPL");  



